跳到主要内容

06 ICU外部中断实验

在这里插入图片描述

开发板官网https://www.edevkit.com/)

本系列教程目录:


ICU外部中断实验

本章目标:

  • 熟悉 RASC 配置 ICU 外部中断的操作使用
  • 掌握 GPIO 的 ICU 配置 API 的使用
  • 掌握 ICU 触发回调的使用

硬件设计

本实验使用开发板上的按键,通过 ICU 外部中断检测按键状态,控制 LED 灯的翻转。

软件设计

新建工程

外部中断 GPIO 检测需要用到 LED,复制一份“04 GPIO-led”修改文件夹名“06 ICU_External_IRQ”,双击打开“EBF_RA6M5.uvprojx”工程,通过 Keil 的新建文件功能新建 key_irq.ckey_irq.h,保存到工程目录下的 \src 文件夹下。

插图01

将代码文件放到 “src” 文件夹下之后,Keil 软件并不会自动将它们加入到工程,这时候需要打开 RASC FSP 配置界面,点击一次中间的 “Generate Project Content” 按钮,自动生成文件架构。从而 “src” 文件夹下的代码文件就会被自动加入进工程中。

插图02

接着关闭 FSP 配置界面返回到 Keil,进行一次编译后会弹出一个提示框提示工程结构发生了变化,点击“是”即可。

插图03

插图04

最后 key_irq.ckey_irq.h 加入到工程中。

FSP 配置

外部中断连接的引脚 IO 是 P105,需要配置 ICU 外设模块,把引脚与 ICU 中断通道 IRQ00 连接起来,使能中断后,检测到 IO 引脚边沿电平时会触发中断。

查看 RA6M5 数据手册的引脚中断表,看到 P105 对应 IRQ0。

插图05

Keil 界面选择“Tools”→“RA Smart Configurator”打开 RASC 界面。

在 FSP 配置界面里面点开 “Pins”→“Peripherals”→“Input”→“ICU0”,在“input/output”选择 IRQ00 通道。

插图06

①选择“Pins”,包含所有 IO 和外围设备。

②选择“Peripherals”外围设备。

③选择 ICU 的 ICU0。

④“operation Mode”选择“Enable”使能 ICU。

⑤选择 IRQ00 通道,配置选择 P105 引脚。

引脚通道配置完,还需要添加外部中断 r_icu 的 Stack 模块。

插图07

①选择“Stack”。

②选择“New Stack”,添加外设模块。

③选择“Input”模块。

④选择“Input”模块的 r_icu 功能,新建一个模块。

插图08

①新建了一个“g_external_irq0”模块。

②点击这个模块,显示“Properties”属性界面。

③属性配置列表,按上图进行配置,特别注意“callback”需要填写回调的函数名,需要用户定义。

External IRQ 属性描述
Name名称
Channel通道(IRQ中断号)
Trigger触发方式:上升沿、下降沿、双边沿或低电平触发
Digital Filtering数字滤波使能
Digital Filtering Sample Clock
(Only valid when Digital Filtering is Enabled)
数字滤波采样时钟
Callback中断回调函数,用户定义函数名
Pin Interrupt Priority中断优先级

配置完成之后可以按下快捷键“Ctrl + S”保存,最后点击 “Generate Project Content” 按钮,让软件自动生成配置代码即可。

  1. 引脚配置信息

引脚配置还是在 pin_data.c 文件里生成。

const ioport_pin_cfg_t g_bsp_pin_cfg_data[] = {
{
.pin = BSP_IO_PORT_01_PIN_05,
.pin_cfg = ((uint32_t) IOPORT_CFG_IRQ_ENABLE | (uint32_t) IOPORT_CFG_PORT_DIRECTION_INPUT)
},
};
  1. ICU配置信息

ICU 模块信息会在 common_data.c 文件里生成。在 RASC 里配置的 IRQ 通道、触发方式和中断服务函数等,都会在 common_data.c 生成一个 external_irq_cfg_t 结构体,里面的内容跟配置时选择的参数一致。

#include "common_data.h"
icu_instance_ctrl_t g_external_irq0_ctrl;

/** External IRQ extended configuration for ICU HAL driver */
const icu_extended_cfg_t g_external_irq0_ext_cfg =
{
.filter_src = EXTERNAL_IRQ_DIGITAL_FILTER_PCLK_DIV,
};

const external_irq_cfg_t g_external_irq0_cfg =
{
.channel = 0,
.trigger = EXTERNAL_IRQ_TRIG_RISING,
.filter_enable = false,
.clock_source_div = EXTERNAL_IRQ_CLOCK_SOURCE_DIV_64,
.p_callback = key_external_irq_callback,
/** If NULL then do not add & */
#if defined(NULL)
.p_context = NULL,
#else
.p_context = &NULL,
#endif
.p_extend = (void *)&g_external_irq0_ext_cfg,
.ipl = (12),
#if defined(VECTOR_NUMBER_ICU_IRQ0)
.irq = VECTOR_NUMBER_ICU_IRQ0,
#else
.irq = FSP_INVALID_VECTOR,
#endif
};
/* Instance structure to use this module. */
const external_irq_instance_t g_external_irq0 =
{
.p_ctrl = &g_external_irq0_ctrl,
.p_cfg = &g_external_irq0_cfg,
.p_api = &g_external_irq_on_icu
};
  1. 中断回调定义

中断回调函数的原型已经在 common_data.h 中进行了声明,需要用户在自己的程序中实现它。

void key_external_irq_callback(external_irq_callback_args_t *p_args);

其参数类型是 external_irq_callback_args_t 结构体指针,这个结构体的原型如下:

typedef struct st_external_irq_callback_args
{
/** Placeholder for user data. Set in @ref external_irq_api_t::open function in @ref external_irq_cfg_t. */
void const * p_context;
uint32_t channel; ///< The physical hardware channel that caused the interrupt.
} external_irq_callback_args_t;

这个结构体仅表明外部中断的内容(context)和通道(channel),而 context 在初始化时被设置为 NULL,没有使用。因而能使用的只有通道值:用来判定发生了哪个通道的中断。

程序设计

程序的入口函数 main 函数调用了 hal_entry 函数。打开 hal_entry.c 文件,在 hal_entry 函数里面编写代码。

实现按键引脚的外部中断检测,配置上升沿触发中断,相当于按键按下松开才会产生上升沿,触发中断调用中断回调函数,判断是否是通道 0,把中断标识置 1,循环中判断中断标识,中断一次就把 LED 灯翻转一次。

  1. 打开引脚的 ICU 模块
fsp_err_t R_ICU_ExternalIrqOpen (external_irq_ctrl_t * const p_api_ctrl, external_irq_cfg_t const * const p_cfg);

外部中断 open 函数用于配置外部中断的类型、设置回调函数、设置通道等,第一个参数是 external_irq_ctrl_t 类型的,第二个参数是 external_irq_cfg_t 的,这个结构体描述了外部中断的通道、优先级、中断类型、触发方式等。

  1. 使能外部中断
fsp_err_t R_ICU_ExternalIrqEnable (external_irq_ctrl_t * const p_api_ctrl);

使能和禁止外部中断的函数,只需要传入一个外部中断接口控制结构体 external_irq_ctrl_t 类型的参数。在 bsp_io.h 中定义了 bsp_io_level_t 电平状态值。

key_irq.h 头文件里定义中断初始化函数。

#ifndef __BSP_KEY_EXTERNAL_IRQ_H
#define __BSP_KEY_EXTERNAL_IRQ_H
#include "hal_data.h"


// KEY 外部中断初始化函数
void key_IRQ_Init(void);

#endif

key_irq.c 里实现初始化函数和中断回调函数。

#include "key_irq.h"

// KEY 外部中断初始化函数
void key_IRQ_Init(void)
{
fsp_err_t err = FSP_SUCCESS;

// 打开ICU 模式
err = R_ICU_ExternalIrqOpen(&g_external_irq0_ctrl, &g_external_irq0_cfg);
// 允许中断
err = R_ICU_ExternalIrqEnable(&g_external_irq0_ctrl);
}

volatile bool key1_press = false;

//按键中断回调函数
void key_external_irq_callback(external_irq_callback_args_t *p_args)
{
// 判断中断通道
if (p_args->channel == 0)
{
key1_press = true; // 按键KEY1按下
}
}

hal_entry.c 文件中添加头文件 key_irq.h,在 hal_entry 入口函数初始化 key_irq,在循环中扫描按键标识,检测到按下就标识置 0 并翻转 LED 灯状态。

extern volatile bool key1_press;
void hal_entry(void)
{
/* TODO: add your own code here */

led_Init(); // LED 初始化
key_IRQ_Init(); // KEY 外部中断初始化

while(1)
{
//判断按键 KEY1_SW2 是否被按下
if (key1_press)
{
key1_press = false; //标志位清零

LED1_TOGGLE(); //LED1 翻转
}

R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒

}


#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}

下载验证

连接下载器,将程序编译并下载到开发板之后,按下复位按键来复位开发板,可以观察到按下松开按键 LED 灯亮,再次按键 LED 灯灭,重复操作,LED 灯状态翻转切换。